﻿
//#define ALLOW_DRAG_DROP
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Configuration;
using System.Xml;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.IO;
using VersionComparer.DataModel;
using System.Threading;
using System.Threading.Tasks;
using VersionComparer.Configuration;
using VersionComparer.UI;
using VersionComparer.ViewModel;
using VersionComparer.Base;
using VersionComparer.DataModel.Actions;
using VersionComparer.UI.ExtendedTreeView;
using VersionComparer.Views;
using VersionComparer.Comparer;
using VersionComparer.Images;


namespace VersionComparer
{
    public partial class MainForm : Form
    {
        
        #region fields
        List<ComparableItem> currentItems = new List<ComparableItem>();
        List<string> knownFiles = new List<string>();
        AutoCompleteCustomSource autoCompleteCustomSource = new AutoCompleteCustomSource();
        private Filter currentFilter;
        List<ComparableItemTreeNode> nodesToPoll = new List<ComparableItemTreeNode>();
        bool disableTextAutoSelection = false;
        ExtendedTreeView selectedTreeView;
        SearchForm searchForm = new SearchForm();
        ItemNamePairLists itemsList = ItemNamePairLists.Load();
        #endregion fields

        #region constructors
        public MainForm()
        {
            InitializeComponent();
            tree1.ShowNodeToolTips = true;
            tree2.ShowNodeToolTips = true;
            DoubleBuffered = true;
            if (RunningAsCommandLine)
            {
                WindowState = FormWindowState.Minimized;
                Visible = false;
                Size = new System.Drawing.Size(0, 0);
                ShowInTaskbar = false;
            }
            else
                WindowState = FormWindowState.Maximized;

            tree1.KeyPress += new KeyPressEventHandler(tree_KeyPress);
            tree2.KeyPress += new KeyPressEventHandler(tree_KeyPress);
            ComparisonEngine.Current.ComparisonStarting += Current_ComparisonStarting;
            ComparisonEngine.Current.ComparisonFinished += Current_ComparisonFinished;
            ComparisonEngine.Current.ItemProcessed += Current_ItemProcessed;
            ComparisonEngine.Current.UpdateProgress += Current_UpdateProgress;
            //FillMenuSpecificActions();
            ThreadManager.Current.StartNew(FileWathcherJob);

            if (Settings.Current.ToggleSides)
                ToggleSides(false);


            cbText1.AutoCompleteMode = AutoCompleteMode.Append;
            cbText2.AutoCompleteMode = AutoCompleteMode.Append;
            cbText1.AutoCompleteSource = AutoCompleteSource.CustomSource;
            cbText2.AutoCompleteSource = AutoCompleteSource.CustomSource;
            cbText1.AutoCompleteCustomSource = autoCompleteCustomSource;
            cbText2.AutoCompleteCustomSource = autoCompleteCustomSource;
            RefreshErrorCount();
        }

        void Current_UpdateProgress(object sender, UpdateProgressEventArgs e)
        {
            SetProgress("", e.Step, e.MaxSteps);
        }
       
        #endregion constructors

        #region properties
        /// <summary>
        /// Returns true if the comparison engine is currently executing a refresh, false otherwise
        /// </summary>
        bool IsProcessing
        {
            get
            {
                return ComparisonEngine.Current.IsProcessing;
            }
        }

        /// <summary>
        /// Returns true if a node is being changed, false otherwise
        /// </summary>
        private bool IsEditing
        {
            get;
            set;
        }
        /// <summary>
        /// Returns true if the filter textbox is visible, false otherwise
        /// </summary>
        private bool FilterIsActive
        {
            get
            {
                return filterGroupBox.Visible;
            }
            set
            {
                if (value != FilterIsActive)
                {

                    filterGroupBox.Visible = value;
                    itemGroupBox.Visible = !value;
                    if (!value)
                    {
                        tree1.Focus();
                        ChangeFilter(null);
                    }
                    else
                    {
                        txtFilter.Focus();
                        //tree1.SaveState();
                        //tree2.SaveState();
                    }
                }
            }

        }
        #endregion properties

        #region action related
        private void ExecuteAction ( object sender , EventArgs args )
        {
            VersionComparer.DataModel.Action action = ((ToolStripMenuItem)sender).Tag as VersionComparer.DataModel.Action;
            action.Execute(this, FocusedSelectedItems().ToArray(), UnfocusedSelectedItems().ToArray());
        }

      
        #endregion action related

        #region comparison engine related

        void Current_ItemProcessed(object sender, ItemEventArgs e)
        {
            this.Execute(UpdateProgress, e.Item);
        }



        void Current_ComparisonFinished(object sender, ComparisonFinishedEventArgs e)
        {
            this.Execute(RefreshFinished, e.Items);
        }

        private void InitializeTreeNodes(ExtendedTreeView  tree, ComparableItem []items)
        {
            List<ComparableItemTreeNode> newNodes = new List<ComparableItemTreeNode> ();
            foreach ( var item in items)
                newNodes.Add (ComparableItemTreeNode.Create ( tree, item));
            tree.SetNodes ( newNodes.ToArray ());
           
        }
        private void ComparisonStarting(ItemNamePairList itemNames, ComparableItem[] items)
        {
            AddToMRUList(itemNames);
            toolStripSplitRefresh.Text = "&Cancel";
            List<ComparableItem> relatedItems = new List<ComparableItem> ();
            foreach (var item in items)
            {
                item.ResetRules();
                item.RelatedItem.ResetRules();
                relatedItems.Add(item.RelatedItem);
            }

            InitializeTreeNodes(tree1, items);
            InitializeTreeNodes(tree2, relatedItems.ToArray());
            lock (currentItems)
            {
                currentItems.Clear();
                currentItems.AddRange(items);
            }
            toolstripProgressBar.Value = 0;
            toolstripProgressBar.Maximum = items.Length;
            toolstripProgressBar.Visible = true;
            toolstripTotalWarningCount.Visible = !toolstripProgressBar.Visible;
            toolstripTotalErrorCount.Visible = !toolstripProgressBar.Visible;
            toolstripTotalItemCount.Visible = !toolstripProgressBar.Visible;
            //itemGroupBox.Enabled = false;
            filterGroupBox.Enabled = false;
            contextMenu1.Enabled = false;
        }
        void Current_ComparisonStarting(object sender, ComparisonStartingEventArgs e)
        {
            this.Execute(ComparisonStarting, e.ItemNames, e.Items);
        }
        #endregion comparison engine related
       
        #region console mode related

        private void WriteArgumentDescription()
        {
            WriteToConsole("\r\n\r\nCompares two set of files. Current user settings are used", ConsoleColor.White);
            WriteToConsole("\r\n\r\n\rUsage:", ConsoleColor.Yellow);
            WriteToConsole("\r\nComparer <<from>>|<<listname>> [<<to>>]", ConsoleColor.Gray);
            WriteToConsole("",ConsoleColor.Gray);
            WriteToConsole("\r\nExample:", ConsoleColor.Yellow);
            WriteToConsole("\r\nVersionComparer \"c:\\from\\test1.xml;c:\\from\\test2.xml\" \"c:\\to\\test1.xml;c:\\to\\test2.xml\"\r\n", ConsoleColor.Gray);

        }
        public string FirstArgument
        {
            get
            {
                if (System.Environment.GetCommandLineArgs().Length > 1)
                    return System.Environment.GetCommandLineArgs()[1];
                else
                    return null;
            }
        }
        public string SecondArgument
        {
            get
            {
                if (System.Environment.GetCommandLineArgs().Length > 2)
                    return System.Environment.GetCommandLineArgs()[2];
                else
                    return null;
            }
        }
        public bool RunningAsCommandLine
        {
            get
            {
                return System.Environment.GetCommandLineArgs().Length > 1;
            }
        }

        private void LogItemDetailsToConsole(ComparableItem item)
        {
            if (item.Difference != Difference.None)
            {
                WriteToConsole(string.Format("Item {0}:", item.Difference.GetDescription(), item.Text), ConsoleColor.Red, false);
                WriteToConsole(string.Format(" {0} ", item.Text), ConsoleColor.Cyan, false);
                WriteToConsole(string.Format(" <=> "), ConsoleColor.Gray, false);
                WriteToConsole(string.Format(" {0} ", item.RelatedItem.Text), ConsoleColor.White, true);

            }

            foreach (var subitem in item.SubItems)
                LogItemDetailsToConsole(subitem);
        }
        private void LogNodeToConsole(ComparableItem item, bool from)
        {
            
            if (from)
            {
                string text = string.Format("{0}", item.Text);
                
                string text2 = string.Format("{0}", item.RelatedItem.Text);

                ConsoleColor color = item.ErrorCount > 0 ? ConsoleColor.Red : item.IgnorableErrorCount > 0 ? ConsoleColor.Gray : ConsoleColor.Green;
                WriteToConsole(string.Format ("{0} errors found in files: " , item.ErrorCount > 0?item.ErrorCount.ToString () : "No"), color,false);
                WriteToConsole(text, ConsoleColor.Cyan, false);
                WriteToConsole(string.Format(" <=> "), ConsoleColor.Gray, false);
                WriteToConsole(text2, ConsoleColor.White);

                if (item.ErrorCount > 0)
                {
                    WriteToConsole("", ConsoleColor.Red);
                    LogItemDetailsToConsole(item);
                }
            }

        }
        private static void WriteToConsole(string text, ConsoleColor color, bool endLine = true)
        {
            var previousColor = Console.ForegroundColor;
            Console.ForegroundColor = color;
            if ( endLine)
                Console.WriteLine(text);
            else
                Console.Write(text);
            Console.ForegroundColor = previousColor;

        }
        #endregion console mode related

        #region error handle and notification related
        public int TotalErrors
        {
            get;
            set;
        }
        public int IgnorableErrors
        {
            get;
            set;
        }
        public void RefreshErrorCount()
        {
            int count = 0;
            int ignorableCount = 0;
            foreach (ComparableItemTreeNode node in tree1.Nodes)
            {
                count += node.Item.ErrorCount;
                ignorableCount += node.Item.IgnorableErrorCount;
            }
            IgnorableErrors = ignorableCount;
            TotalErrors = count;
            
            toolstripTotalErrorCount.ForeColor = TotalErrors > 0? Color.DarkRed : ( IgnorableErrors > 0 ? Color.DarkOrange : Color.Green);
            toolstripTotalWarningCount.ForeColor = (IgnorableErrors > 0 ? Color.DarkOrange : Color.Green);
            

            toolstripTotalErrorCount.Text = TotalErrors.ToString ();
            toolstripTotalItemCount.Text = GetCurrentItemList(false) != null ? GetCurrentItemList(false).Count.ToString() : "0";
            toolstripTotalWarningCount.Text = IgnorableErrors.ToString() ;
            
        }
        #endregion error handle and notification related
       
        #region progress related methods
        private void UpdateProgress(ComparableItem item)
        {
            Filter filter = CurrentFilter;
            var node = ComparableItemTreeNode.Create(tree1, item, filter);
            tree1.RefreshNode(node);
            node = ComparableItemTreeNode.Create(tree2, item.RelatedItem, filter);
            
            tree2.RefreshNode(node);
            if (RunningAsCommandLine)
            {
                LogNodeToConsole(item, true);
                LogNodeToConsole(item.RelatedItem , false);
                Console.WriteLine();
            }
        }
        private void RefreshFinished(ComparableItem [] items)
        {
            if (IsDisposed)
                return;

            //itemGroupBox.Enabled = true;
            filterGroupBox.Enabled = true;
            contextMenu1.Enabled = true;
            lock (nodesToPoll)
            {
                nodesToPoll.Clear();
                foreach (ComparableItemTreeNode node in tree1.Nodes)
                    nodesToPoll.Add(node);
                foreach (ComparableItemTreeNode node in tree2.Nodes)
                    nodesToPoll.Add(node);
            }
            RefreshErrorCount();
            toolStripSplitRefresh.Text = "&Refresh";
            if (tree1.SelectedNode == null && tree1.Nodes.Count > 0)
                tree1.SelectedNode = tree1.Nodes[0];

            toolstripProgressBar.Visible = false;
            toolstripTotalWarningCount.Visible = !toolstripProgressBar.Visible;
            toolstripTotalErrorCount.Visible = !toolstripProgressBar.Visible;
            toolstripTotalItemCount.Visible = !toolstripProgressBar.Visible;

            if (RunningAsCommandLine)
            {
                string message = "Terminated. ";
                if ( TotalErrors > 0 )
                    message += TotalErrors.ToString ();
                else
                    message += "No";
                message += " errors found";
                WriteToConsole(message, TotalErrors > 0 ? ConsoleColor.Red : IgnorableErrors > 0 ? ConsoleColor.Gray : ConsoleColor.Green);
                Close();
            }
            
        }
        private void SetProgressSync(string stepText, int currentStep, int? maxSteps = null)
        {
            if (maxSteps != null)
            {
                if (toolstripProgressBar.Maximum != maxSteps.Value)
                {
                    //toolstripProgressBar.Style = maxSteps.Value == 0? ProgressBarStyle.Marquee : ProgressBarStyle.Continuous;
                    //toolstripProgressBar.MarqueeAnimationSpeed = -1;
                    toolstripProgressBar.Maximum = maxSteps.Value;
                }
                
                    
            }
            
            
            bool visible;
            if (currentStep > toolstripProgressBar.Maximum)
                visible = false;
            else
            {
                toolstripProgressBar.Value = currentStep;
                visible = true;
                toolstripProgressBar.ToolTipText = stepText;
            }

            if (visible != toolstripProgressBar.Visible)
            {
                toolstripProgressBar.Visible = visible;
                toolstripTotalWarningCount.Visible = !toolstripProgressBar.Visible;
                toolstripTotalErrorCount.Visible = !toolstripProgressBar.Visible;
                toolstripTotalItemCount.Visible = !toolstripProgressBar.Visible;
                Invalidate();
            }


        }
        public void SetProgress(string stepText, int currentStep, int? maxSteps = null)
        {
            this.Execute ( ()=>SetProgressSync(stepText , currentStep, maxSteps ));
            
        }
        #endregion progress related methods

        #region MRU related
        public void AddToMRUList(ItemNamePairList items)
        {
            ItemId[] items1 = items.Items1;
            ItemId[] items2 = items.Items2;
            var currentItemList = GetCurrentItemList(false);
            if (currentItemList == null || !currentItemList.IsEqualTo(items1, items2))
            {
                itemsList.Add(items1, items2);
                RefreshCombos(true);
            }
            itemsList.Select(items1, items2);
        }
        private void SetItems(ItemId[] items, ComboBox combobox)
        {
            StringBuilder builder = new StringBuilder();
            foreach (var item in items)
            {
                builder.AppendFormat("{0};", item.Name);
            }
            combobox.Text = builder.ToString().Trim(';');
        }
        private ItemId[] GetItems( ComboBox combobox)
        {
            string[] aux = new string[0];
            List<ItemId> result = new List<ItemId>();
            string currentItemlistName = null;
            List<ItemId > currentItemlistItems = new List<ItemId> ();
            this.Execute(()=>
            {
                if (cbText1 == combobox)
                {
                    var currentItemlist = cbText1.SelectedItem as ItemNamePairList;
                    if (currentItemlist != null)
                    {
                        currentItemlistName = currentItemlist.Name1;
                        currentItemlistItems = new List<ItemId> ( currentItemlist.Items1);
                    }
                }
                else
                {
                    var currentItemlist = cbText2.SelectedItem as ItemNamePairList;
                    if (currentItemlist != null)
                    {
                        currentItemlistName = currentItemlist.Name2;
                        currentItemlistItems = new List<ItemId> (  currentItemlist.Items2);
                    }
                }
                aux = combobox.Text.Split(";".ToCharArray());
            });
            foreach (var value in aux)
            {
                ItemId item = currentItemlistItems.FirstOrDefault(x => x.Name == value);
                if (item != null)
                {
                    result.Add(item);
                    currentItemlistItems.Remove(item);
                }
                else
                    result.Add(new ItemId (value));
            }

            return result.ToArray ();
        }
        private ItemId[] Items1
        {
            get
            {
                return GetItems(cbText1);
            }
            set
            {
                SetItems(value, cbText1);
            }
        }
        private ItemId[] Items2
        {
            get
            {
                return GetItems(cbText2);
            }
            set
            {
                SetItems(value, cbText2);
            }
        }
        #endregion MRU related

        #region event handling

        protected override void OnClosing(CancelEventArgs e)
        {
            ThreadManager.Current.Stop(FileWathcherJob);
            base.OnClosing(e);
        }
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            Text = Resources.Resources.VersionComparerCaption;
            RefreshCombos(true);
            //RefreshActions();
            tree1.Scrolled += new EventHandler(tree1_Scrolled);
            tree2.Scrolled += new EventHandler(tree2_Scrolled);
            tree1.ContextMenuStrip.Tag = tree1;
            tree2.ContextMenuStrip.Tag = tree2;
            if (RunningAsCommandLine)
            {
                string firstArgument = FirstArgument;
                if (firstArgument == "/?")
                {
                    WriteArgumentDescription();
                    Exit();
                    return;
                }
                WriteToConsole("\r\n\r\n\r\n", ConsoleColor.Yellow);
                

                if (!String.IsNullOrEmpty(SecondArgument))
                {
                    //cbText2.Text = SecondArgument;
                    
                    ItemNamePairList newItemsList = new ItemNamePairList();
                    newItemsList.Set(null, FirstArgument.Split(";".ToArray()), SecondArgument.Split(";".ToArray()));
                    itemsList.Add(newItemsList);
                    cbText1.SelectedItem = AddListToCombox(newItemsList);
                }
                else
                    cbText1.Text = firstArgument;
                this.Enabled = false;
                this.Visible = false;
            }

            //if (CurrentFileList != null )
            RefreshDifferences();
        }
        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            if ((tree1.Focused || tree2.Focused) && keyData == Keys.Space)
            {
                var treeview = GetCurrentTreeView();
                if (treeview.SelectedNode != null)
                {
                    if (treeview.SelectedNode.IsExpanded)
                        treeview.SelectedNode.Collapse();
                    else
                    {
                        treeview.BeginUpdate();
                        (treeview.SelectedNode as SelectableTreeNode).ExpandAll();
                        treeview.EndUpdate();
                    }
                    return true;
                }
            }

            //Ctrl + F

            if (keyData == (Keys)131142)
            {
                Search(true, true);
                return true;
            }
            if (keyData == Keys.Escape)
            {
                if (IsEditing)
                    IsEditing = false;
                if (FilterIsActive)
                {
                    FilterIsActive = false;
                    return true;
                }
            }


            if (keyData == Keys.Enter && !IsEditing)
            {

                RefreshDifferences();
                return true;
            }
            if (Control.ModifierKeys == 0 && !FilterIsActive)
            {

                if (keyData >= Keys.F1 && keyData < Keys.F12)
                {
                }
            }
            bool processed = base.ProcessCmdKey(ref msg, keyData);


            return processed;
        }

        #region treeview related
        void tree2_Scrolled(object sender, EventArgs e)
        {
            tree1.SetHorizontalScrollPos(tree2.GetHorizontalScrollPos());
            tree1.SetVerticalScrollPos(tree2.GetVerticalScrollPos());
        }
        void tree1_Scrolled(object sender, EventArgs e)
        {
            tree2.SetHorizontalScrollPos(tree1.GetHorizontalScrollPos());
            tree2.SetVerticalScrollPos(tree1.GetVerticalScrollPos());
            tree2.Refresh();

        }
        void tree_KeyPress(object sender, KeyPressEventArgs e)
        {
            bool processing = ComparisonEngine.Current.IsProcessing;
           
            if (!processing && !FilterIsActive)
            {
                if (char.IsLetterOrDigit(e.KeyChar))
                {
                    FilterIsActive = true;
                    txtFilter.Text = "" + e.KeyChar;
                    txtFilter.SelectionStart = 1;
                }
            }
        }

        private void tree1_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
        {
            if (!IsEditing)
                return;
            IsEditing = false;
            if (ComparisonEngine.Current.IsProcessing || e.Node == null || e.Node.Tag == null)
                return;
            if (e.Label != e.Node.Text)
            {
                try
                {
                    ComparableItemTreeNode node = e.Node as ComparableItemTreeNode;
                    if (node.Item.ChangeValue(e.Label))
                        RefreshDifferences(true);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    RefreshDifferences(true);
                }
            }
        }
        private void tree1_BeforeLabelEdit(object sender, NodeLabelEditEventArgs e)
        {
            if (e.Node == null || e.Node.Tag == null)
                return;
            ComparableItemTreeNode node = e.Node as ComparableItemTreeNode;
            if (ComparisonEngine.Current.IsProcessing || !node.Item.AllowsChangingValue)
                e.CancelEdit = true;
            IsEditing = !e.CancelEdit;
        }
        
        private void tree1_AfterCheck(object sender, TreeViewEventArgs e)
        {

        }
        private void tree2_AfterCheck(object sender, TreeViewEventArgs e)
        {

        }

        private void tree2_AfterSelect(object sender, TreeViewEventArgs e)
        {
            //FillMenuSpecificActions();
            Synchronize(tree2, tree1, e.Node as ComparableItemTreeNode);
        }
        private void tree2_AfterExpand(object sender, TreeViewEventArgs e)
        {
            Synchronize(tree2, tree1, e.Node as ComparableItemTreeNode);
        }
        private void tree2_AfterCollapse(object sender, TreeViewEventArgs e)
        {
            Synchronize(tree2, tree1, e.Node as ComparableItemTreeNode);
        }
        private void tree1_AfterSelect(object sender, TreeViewEventArgs e)
        {
            //FillMenuSpecificActions();
            Synchronize(tree1, tree2, e.Node as ComparableItemTreeNode);
            UpdateLabelStatus();
        }
        private void tree1_AfterCollapse(object sender, TreeViewEventArgs e)
        {
            Synchronize(tree1, tree2, e.Node as ComparableItemTreeNode);
        }
        private void tree1_AfterExpand(object sender, TreeViewEventArgs e)
        {
            Synchronize(tree1, tree2, e.Node as ComparableItemTreeNode);
        }
        #endregion treeview related

        private void UpdateLabelStatus()
        {
            var node1 = (tree1.SelectedNode as ComparableItemTreeNode);
            var node2 = (tree2.SelectedNode as ComparableItemTreeNode);
            if (Settings.Current.ToggleSides)
            {
                var aux = node1;
                node1 = node2;
                node2 = aux;
            }


            if (node1 == null)
            {
                toolstripIcon1.Image = null;
                toolstripText1.Text = "";
            }
            else
            {
                var item = node1.Item;
                toolstripIcon1.Image = node1.Item.Image;
                if (item.Exists)
                    toolstripText1.Text = node1.Text;
                else
                    toolstripText1.Text = "";
            }

            
            if (node2 == null)
            {
                toolstripIcon2.Image = null;
                toolstripText2.Text = "";
            }
            else
            {
                var item = node2.Item;
                toolstripIcon2.Image = item.Image;
                if (item.Exists)
                    toolstripText2.Text = node2.Text;
                else
                    toolstripText2.Text = "";
            }
        }

        #region drag & drop related
        private void button1_Click(object sender, EventArgs e)
        {
            OpenFiles();
        }
        private void Form1_DragDrop(object sender, DragEventArgs e)
        {
#if ALLOW_DRAG_DROP
            try
            {
                string[] files = (string[])e.Data.GetData("FileDrop");
                if (files != null)
                {
                    if (files.Length > 0)
                    {
                        cbText1.Text = files[0];
                    }
                    if (files.Length > 1)
                    {
                        cbText2.Text = files[1];
                        RefreshDifferences();
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
#endif
        }
        private void Form1_DragEnter(object sender, DragEventArgs e)
        {
#if ALLOW_DRAG_DROP
            e.Effect = DragDropEffects.Link;
#endif
        }
        #endregion drag & drop related

        private List<ComparableItemTreeNode> OrderedSelectedNodes(TreeNodeCollection nodes)
        {
            List<ComparableItemTreeNode> result = new List<ComparableItemTreeNode>();
            foreach (ComparableItemTreeNode node in nodes)
            {
                if (node != null && (node.Selected || node.IsSelected ))
                    result.Add(node);
                result.AddRange(OrderedSelectedNodes(node.Nodes));
            }
            
            return result;
        }
        private ComparableItemTreeNode SelectedNode
        {
            get
            {
                var tree = GetCurrentTreeView ();
                if (tree != null)
                    return tree.SelectedNode as ComparableItemTreeNode;
                else
                    return null;
            }
        }
        private void cbText1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (disableTextAutoSelection)
                return;
            if (cbText1.SelectedIndex > 0)
            {
                var nodeList = (cbText1.SelectedItem as ItemNamePairList);
                if (nodeList != null)
                {
                    nodeList.LastUsed = DateTime.Now;
                    itemsList.Save();
                }
            }
            cbText2.SelectedIndex = cbText1.SelectedIndex;
            RefreshDifferences(true);
            
        }
        private void mnuExpandAll_Click(object sender, EventArgs e)
        {
            ExpandAll();
           
        }
        private void cbText2_TextChanged(object sender, EventArgs e)
        {

            string newtext = NormalizeList(cbText2.Text);
            if (cbText2.Text != newtext)
            {
                cbText2.Text = newtext;
                if (cbText2.SelectedItem == null || cbText2.SelectedItem.ToString() != newtext)
                {
                    for (int i = 0; i < cbText2.Items.Count; i++)
                    {
                        if (cbText2.Items[i].ToString () == newtext)
                        {
                            cbText2.SelectedIndex = i;
                            break;
                        }
                    }
                }
            }
            else
            {
                if (autoCompleteCustomSource.Update(newtext))
                {
                }
            }
        }
        private void cbText1_TextChanged(object sender, EventArgs e)
        {
            string newtext = NormalizeList(cbText1.Text);
            disableTextAutoSelection = true;
            if (cbText1.Text != newtext)
                cbText1.Text = newtext;
            else
                autoCompleteCustomSource.Update(newtext);
            disableTextAutoSelection = false;
        }

        /*
          
         */
        private void btLoadList_Click(object sender, EventArgs e)
        {
            ManageItemsList();
        }
        private void tree1_Enter(object sender, EventArgs e)
        {
            selectedTreeView = tree1;
        }
        private void tree2_Enter(object sender, EventArgs e)
        {
            selectedTreeView = tree2;
        }
        private void previousToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Search(false, false);
        }
        

        private void ShowSettingsDialog()
        {
            ControlContainerForm<AllSettingsControl> form = new ControlContainerForm<AllSettingsControl>();
            if (form.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                ComparisonEngine.Current.ClearCache();
                RefreshDifferences();
            }
        }
      
        private void mnuRemoveRulesFromNode_Click(object sender, EventArgs e)
        {
            RemoveAllRules();
        }

      
        private void btCancelFilter_Click(object sender, EventArgs e)
        {
            FilterIsActive = false;
        }


        
        private void FilterTreeViews(ManagedThread threadManager, Filter filter)
        {
            if (filter == null)
                filter = new Filter();
            List<ComparableItem> items;
            lock ( currentItems )
            {
                items = new List<ComparableItem> (currentItems );
            }
            List<ComparableItemTreeNode> nodesToRefresh1 = new List<ComparableItemTreeNode>();
            List<ComparableItemTreeNode> nodesToRefresh2 = new List<ComparableItemTreeNode>();
            foreach (var item in items)
            {
                if (filter.Cancel || threadManager.CancellationPending )
                    return;
                var node1 = ComparableItemTreeNode.Create(tree1, item, filter);
                if (node1 != null)
                    nodesToRefresh1.Add(node1);
                var node2 = ComparableItemTreeNode.Create(tree2, item.RelatedItem, filter);
                if (node2 != null)
                    nodesToRefresh2.Add(node2);
            }
            tree1.SetNodes(nodesToRefresh1.ToArray());
            tree2.SetNodes(nodesToRefresh2.ToArray());
        }

        private void ChangeFilter(Filter newFilter)
        {
            if (CurrentFilter != null)
                CurrentFilter.Cancel = true;
            if (newFilter == null)
                newFilter = new Filter();
            CurrentFilter = newFilter;
            ThreadManager.Current.StartNew(FilterTreeViews, newFilter);
        }
        private Filter CurrentFilter
        {
            get
            {
                return currentFilter;
            }
            set
            {
                Interlocked.Exchange(ref currentFilter, value);
            }
        }
        private void txtFilter_TextChanged(object sender, EventArgs e)
        {
            if (String.IsNullOrEmpty(txtFilter.Text))
                FilterIsActive = false;
            else
                ChangeFilter(new Filter(txtFilter.Text, !cbMatchCase.Checked));
            
        }
        private void cbText1_Enter(object sender, EventArgs e)
        {
            contextMenu1.Enabled = false;
        }
        private void cbText1_Leave(object sender, EventArgs e)
        {
            contextMenu1.Enabled = true;
        }
        private void cbText2_Leave(object sender, EventArgs e)
        {
            contextMenu1.Enabled = true;
        }
        private void cbText2_Enter(object sender, EventArgs e)
        {
            contextMenu1.Enabled = false;
        }
        #endregion event handling

        #region context menu events
        /// <summary>
        /// Updates the state of the menuItem that controls synchronization
        /// </summary>
        /// <param name="menuItem">menu item to update</param>
        private void UpdateEnforceValueMenu(ToolStripMenuItem menuItem)
        {
            menuItem.Enabled = !IsProcessing && selectedTreeView != null;
            if (selectedTreeView != null)
            {
                Difference difference = Difference.None;
                foreach (ComparableItemTreeNode node in selectedTreeView.SelectedNodes)
                {
                    ComparableItem item = node.Item;
                    difference |= item.Difference;
                    if (!item.AllowsCopying)
                    {
                        menuItem.Enabled = false;
                        break;
                    }
                }

                string directionString = "";
                if (TreeIsSource(selectedTreeView))
                {
                    menuItem.Image = ImageResources.MoveRight;
                    directionString = Resources.Resources.ApplyToDestination;
                }
                else
                {
                    menuItem.Image = ImageResources.MoveLeft;
                    directionString = Resources.Resources.ApplyToSource;
                }
                switch (difference)
                {
                    case Difference.Name:
                    case Difference.NameCase:
                        menuItem.Text = string.Format(Resources.Resources.ApplyNameUpdate, directionString);
                        break;
                    case Difference.Value:
                        menuItem.Text = string.Format(Resources.Resources.ApplyValueUpdate, directionString);
                        break;
                    case Difference.Unexistant:
                        menuItem.Text = string.Format(Resources.Resources.ApplyUnexistantUpdate, directionString);
                        break;

                    case Difference.Unmatched:
                        menuItem.Text = string.Format(Resources.Resources.ApplyUnmatchedUpdate, directionString);
                        break;
                    default:
                        menuItem.Text = string.Format(Resources.Resources.ApplyChangesUpdate, directionString);
                        break;
                }
            }
        }
        private void contextMenuStrip1_Opening(object sender, CancelEventArgs e)
        {
            bool processing = IsProcessing;
            itemListToolStripMenuItem.Enabled = !processing;
            if (processing)
                (sender as ContextMenuStrip).Enabled = false;
            else
                (sender as ContextMenuStrip).Enabled = true;

            UpdateToggleExclusionRuleMenu(mnuExclusionRules);
            UpdateRemoveAllExclusionRuleMenu(mnuRemoveRulesFromNode);
            UpdateEnforceValueMenu(mnuEnforceValue);

            FillMenuSpecificActions(mnuActionsSeparator, contextMenu1.Items );
        }
      
        private void mnuRefresh_Click(object sender, EventArgs e)
        {
            RefreshDifferences();
        }
        private void mnuSearch_Click(object sender, EventArgs e)
        {
            Search(true, false);

        }
        private void mnuSelectAll_Click(object sender, EventArgs e)
        {
            SelectAllItems();
        }
        private void manageItemList_Click(object sender, EventArgs e)
        {
            ManageItemsList();
        }
        private void mnuCollapseAll_Click(object sender, EventArgs e)
        {
            CollapseAll();

        }
        private void mnuExpandNode_Click(object sender, EventArgs e)
        {
            ExpandAll();

        }
        private void mnuEnforceValue_Click(object sender, EventArgs e)
        {
            EnforceValue();
        }
        private void mnuToggleSides_Click(object sender, EventArgs e)
        {
            ToggleSides(true);
        }
        private void mnuManageActions_Click_1(object sender, EventArgs e)
        {
            ManageActions();
        }
        private void mnuRename_Click(object sender, EventArgs e)
        {
            ChangeNodeValue();
        }
        private void mnuRefreshAll_Click(object sender, EventArgs e)
        {
            RefreshDifferences(true);
        }
        private void mnuNextDifference_Click(object sender, EventArgs e)
        {
            SearchDifference(true);
        }
        private void mnuPreviousDifference_Click(object sender, EventArgs e)
        {
            SearchDifference(false);
        }
        private void mnuSettings_Click(object sender, EventArgs e)
        {
            ShowSettingsDialog();

        }
        private void mnuExcludeRule_Click(object sender, EventArgs e)
        {
            ToggleSelectedNodeRule();
        }
        private void mnuCopyToClipboard_Click(object sender, EventArgs e)
        {
            CopyToClipboard();
        }
     
        #endregion context menu events
      
        #region main menu events
        private void mainMnuTools_DropDownOpening(object sender, EventArgs e)
        {
            UpdateEnforceValueMenu(mainMnuSynchronize);
            UpdateToggleExclusionRuleMenu(mnuMainToggleExclusionRule);
            UpdateRemoveAllExclusionRuleMenu(mnuMainRemoveAllRules);
            FillMenuSpecificActions(mnuMainActionsSeparator, mainMnuTools.DropDownItems);
        }
        private void mnuMainSelectAll_Click(object sender, EventArgs e)
        {
            SelectAllItems();
        }
        private void mnuActions_Click(object sender, EventArgs e)
        {
            ManageActions();
        }
        private void mainMnuOpenFileList_Click(object sender, EventArgs e)
        {
            ManageItemsList();
        }
        private void mainMnuOpenSingleFilePair_Click(object sender, EventArgs e)
        {
            OpenFiles();
        }
        private void mainMnuExit_Click(object sender, EventArgs e)
        {
            Exit();
        }
        private void mainMnuFind_Click(object sender, EventArgs e)
        {
            Search(true, true);
        }
        private void mainMnuFindNext_Click(object sender, EventArgs e)
        {
            Search(true, false);
        }
        private void mainMnuFindPrevious_Click(object sender, EventArgs e)
        {
            Search(false, false);
            
        }
        private void mainMnuNextDifference_Click(object sender, EventArgs e)
        {
            SearchDifference(true);
        }
        private void mainMenuPreviousDifference_Click(object sender, EventArgs e)
        {
            SearchDifference(false);
        }
        private void mainMnuExpandAll_Click(object sender, EventArgs e)
        {
            ExpandAll();
        }
        private void mainMnuCollapseAll_Click(object sender, EventArgs e)
        {
            CollapseAll();
        }
        private void mainMnuToggle_Click(object sender, EventArgs e)
        {
            ToggleSides(true);
        }
        private void mnuMainCopyToClipboard_Click(object sender, EventArgs e)
        {
            CopyToClipboard();
        }
        private void mnuMainToggleExclusionRule_Click(object sender, EventArgs e)
        {
            ToggleSelectedNodeRule();

        }
        private void mainMnuSynchronize_Click(object sender, EventArgs e)
        {

        }
        private void mnuMainChangeSelectedValue_Click(object sender, EventArgs e)
        {
            ChangeNodeValue();
        }
        private void mnuMainExclusionRules_Click(object sender, EventArgs e)
        {
            RemoveAllRules();
        }

        private void mnuMainManageActionsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ManageActions();
        }

        private void mainMnuSettings_Click(object sender, EventArgs e)
        {
            ShowSettingsDialog();
        }

        private void refreshToolStripMenuItem_Click(object sender, EventArgs e)
        {
            RefreshDifferences();
        }

        private void mainMnuAbout_Click(object sender, EventArgs e)
        {
            AboutForm form = new AboutForm();
            form.Owner = this;
            form.ShowDialog();
        }
        #endregion main menu events

        #region auxiliary methods
        private void RemoveMenuSpecificActions(ToolStripItemCollection items)
        {
            List<ToolStripMenuItem> actionMenus = new List<ToolStripMenuItem>();
            foreach (var item in items)
            {
                ToolStripMenuItem menu = item as ToolStripMenuItem;
                if (menu != null && menu.Tag is VersionComparer.DataModel.Action)
                    actionMenus.Add(menu);
            }

            foreach (var menu in actionMenus)
                items.Remove(menu);
        }
        private void FillMenuSpecificActions( ToolStripItem mnuSeparator, ToolStripItemCollection items)
        {
            RemoveMenuSpecificActions(items);
            bool added = false;
            List<Keys> alreadyAssigned = new List<Keys>();
            int index = 1;
            int position = 0;
            var focusedSelectedItems = FocusedSelectedItems();
            var unfocusedSelectedItems = UnfocusedSelectedItems();
            List<DataModel.Action> actions = new List<DataModel.Action>();
            actions.AddRange(SpecificActionList.Current);
            actions.AddRange(ActionList.Current);

            foreach (var action in actions)
            {
                if (action.MayBeExecuted(this, focusedSelectedItems, unfocusedSelectedItems))
                {
                    added = true;
                    ToolStripMenuItem menuItem = new ToolStripMenuItem(action.DisplayName, action.Image(this, focusedSelectedItems, unfocusedSelectedItems), ExecuteAction);
                    menuItem.Tag = action;
                    Keys shortcutKey = action.ShortcutKey;
                    if (shortcutKey == Keys.None || alreadyAssigned.Contains(shortcutKey))
                    {
                        shortcutKey = Keys.None;
                        while (index <= 8 && shortcutKey == Keys.None)
                        {
                            Keys aux = Keys.Alt | (Keys.D0 + index);
                            index++;
                            if (!alreadyAssigned.Contains(aux))
                                shortcutKey = aux;
                        }
                    }
                    if (shortcutKey != Keys.None)
                    {
                        alreadyAssigned.Add(shortcutKey);
                        menuItem.ShortcutKeys = shortcutKey;
                    }

                    items.Insert(position++, menuItem);
                }
            }
            mnuSeparator.Visible = added;
        }
        
        /// <summary>
        /// Returns true if the given tree is the source tree, false otherwise
        /// </summary>
        /// <param name="tree">tree to check</param>
        /// <returns>true if tree is the source tree</returns>
        private bool TreeIsSource(TreeView tree)
        {
            return Settings.Current.ToggleSides ? tree == tree2 : tree == tree1;
        }
        /// <summary>
        /// Selects all items
        /// </summary>
        private void SelectAllItems()
        {
            List<TreeNode> nodes = new List<TreeNode>();
            var treeView = GetCurrentTreeView();
            treeView.UnselectAll();
            foreach (ComparableItemTreeNode node in treeView.GetVisibleNodes())
                node.Selected = true;
        }
        /// <summary>
        /// Updates the text of the toggle exclusion rule menu item
        /// </summary>
        /// <param name="menuItem">menu item to set the text</param>
        private void UpdateToggleExclusionRuleMenu(ToolStripMenuItem menuItem)
        {
            if (CurrentNodesHaveRules())
                menuItem.Text = Resources.Resources.RemoveExclusionRule;
            else
                menuItem.Text = Resources.Resources.AddExclusionRule;
        }
        /// <summary>
        /// Sets the state for the remove all exclusion rules menu item
        /// </summary>
        /// <param name="menuItem"></param>
        private void UpdateRemoveAllExclusionRuleMenu(ToolStripMenuItem menuItem)
        {
            bool rulesExist = false;
            var currentItemList = GetCurrentItemList(false);
            if (currentItemList != null)
                rulesExist = currentItemList.Rules.Count > 0;
            menuItem.Text = Resources.Resources.RemoveAllExclusionRules;
            menuItem.Enabled = rulesExist;
            
        }
        /// <summary>
        /// Returns true if all the selected nodes have rules applied to them
        /// </summary>
        /// <returns>true if all the selected nodes have rules applied to them</returns>
        private bool CurrentNodesHaveRules()
        {
            var currentItemList = GetCurrentItemList(false);
            if (currentItemList == null)
                return false;
            ExtendedTreeView treeView = GetCurrentTreeView();
            List<ComparableItem> items = new List<ComparableItem>();
            foreach (ComparableItemTreeNode node in treeView.SelectedNodes)
            {
                if (!currentItemList.Rules.HasRuleApplied(node.Item))
                    return false;
            }
            return true;
        }
        /// <summary>
        /// Searches for the next/previous difference, and optionally opens the search window
        /// </summary>
        /// <param name="next">if true the next difference is searched. If false the previous difference is searched</param>
        /// <param name="forceWindowVisible">if true the search window is shown</param>
        private void Search(bool next, bool forceWindowVisible)
        {
            ExtendedTreeView treeview = GetCurrentTreeView();

            string textToSearch = null;
            if (searchForm.Visible == false && !FilterIsActive)
            {
                if (forceWindowVisible || String.IsNullOrEmpty(searchForm.CurrentText))
                {
                    searchForm = new SearchForm();
                    searchForm.Show(this);
                    if (FilterIsActive)
                        searchForm.DefaultText = txtFilter.Text;
                    else
                        searchForm.DefaultText = treeview.SelectedNode != null ? treeview.SelectedNode.Text : "";
                }
                else
                {
                    textToSearch = searchForm.CurrentText;
                    if (next)
                        searchForm.MoveNext(textToSearch);
                    else
                        searchForm.MovePrevious(textToSearch);
                }
            }
            else
            {
                if (FilterIsActive)
                {
                    searchForm.TreeView = treeview;
                    textToSearch = txtFilter.Text;
                }
                if (next)
                    searchForm.MoveNext(textToSearch);
                else
                    searchForm.MovePrevious(textToSearch);
            }

            if (searchForm.TreeView != treeview)
            {
                searchForm.TreeView = treeview;
                searchForm.Text = tree1 == treeview ? "Search left tree" : "Search right tree";
            }
        }
        /// <summary>
        /// Searches for the next/previous difference
        /// </summary>
        /// <param name="next">if true, the next difference is searched, if false the previous difference is searched</param>
        private void SearchDifference(bool next)
        {

            ExtendedTreeView treeView = GetCurrentTreeView();
            if (treeView.Nodes.Count == 0)
                return;
            ComparableItemTreeNode startingNode = treeView.SelectedNode as ComparableItemTreeNode;
            if (startingNode == null)
                startingNode = treeView.Nodes[0] as ComparableItemTreeNode;

            ComparableItemTreeNode currentNode = startingNode;
            do
            {
                if (next)
                    currentNode = treeView.GetNextNode(currentNode, true) as ComparableItemTreeNode;
                else
                    currentNode = treeView.GetPreviousNode(currentNode) as ComparableItemTreeNode;
                if (currentNode == null)
                    return;
                ComparableItem item1 = currentNode.Item;
                ComparableItem item2 = item1.RelatedItem;
                if ((item1.Difference != Difference.None && item1.ErrorCount > 0 && item1.SubItems.Length == 0)
                    || (item2.Difference != Difference.None && item2.ErrorCount > 0 && item2.SubItems.Length == 0))
                {

                    currentNode.ExpandAll();
                    bool previousEnabled1 = tree1.AutoSelectionEnabled;
                    bool previousEnabled2 = tree2.AutoSelectionEnabled;
                    tree1.AutoSelectionEnabled = false;
                    tree2.AutoSelectionEnabled = false;
                    treeView.SelectNode(currentNode);
                    tree1.AutoSelectionEnabled = previousEnabled1;
                    tree2.AutoSelectionEnabled = previousEnabled2;
                    currentNode.EnsureVisible();

                    return;
                }
            }
            while (currentNode != startingNode);
        }
        /// <summary>
        /// Closes the application
        /// </summary>
        private void Exit()
        {
            Close();
        }
        /// <summary>
        /// Opens two OpenFileDialog to get two files for comparison
        /// </summary>
        private void OpenFiles()
        {
            string files1 = cbText1.Text;
            string files2 = cbText2.Text;

            var currentItemList = GetCurrentItemList(false);
            if (currentItemList != null)
            {
                files1 = ItemNamePairList.ListToText(currentItemList.Items1);
                files2 = ItemNamePairList.ListToText(currentItemList.Items2);
            }
            files1 = LoadFile(files1);
            if (files1 != null)
            {
                files2 = LoadFile(files2);
                if (files2 != null)
                {
                    cbText1.Text = files1;
                    cbText2.Text = files2;
                    RefreshDifferences();
                }
            }
        }
        /// <summary>
        /// Toggles the sides of the treeviews
        /// </summary>
        /// <param name="updateSettings">if true the settings are updated so that next time the app is run, the sides are toggled</param>
        private void ToggleSides(bool updateSettings)
        {
            var control1 = splitContainer1.Panel1.Controls[0];
            var control2 = splitContainer1.Panel2.Controls[0];
            splitContainer1.Panel1.Controls.Clear();
            splitContainer1.Panel2.Controls.Clear();
            splitContainer1.Panel1.Controls.Add(control2);
            splitContainer1.Panel2.Controls.Add(control1);
            UpdateLabelStatus();

            if (updateSettings)
            {
                Settings.Current.ToggleSides = !Settings.Current.ToggleSides;
                Settings.Current.Save();
            }

        }
        /// <summary>
        /// Opens the manage actions UI
        /// </summary>
        private void ManageActions()
        {
            ListManagerForm<BatchAction> form = new ListManagerForm<BatchAction>();
            form.Icon = Icon;
            form.WindowState = FormWindowState.Maximized;

            form.List = ActionList.Current.ToList();
            if (form.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                ActionList.Current.Set(form.List);
                //RefreshActions();
            }
        }
        /// <summary>
        /// Expands all the nodes
        /// </summary>
        private void ExpandAll()
        {
            GetCurrentTreeView().ExpandAll();
        }
        /// <summary>
        /// Collapses all nodes
        /// </summary>
        private void CollapseAll()
        {
            GetCurrentTreeView().CollapseAll();
        }
        /// <summary>
        /// Change the node value
        /// </summary>
        private void ChangeNodeValue()
        {
            GetCurrentTreeView().SelectedNode.BeginEdit();
        }
        /// <summary>
        /// Changes the list of items to compare
        /// </summary>
        private void ManageItemsList()
        {
            if (IsProcessing)
                return;

            FileListInsertForm form = new FileListInsertForm();
            form.FileList = GetCurrentItemList(true);
            form.Owner = this;
            if (form.ShowDialog() == DialogResult.OK)
            {
                itemsList.Add(form.FileList);
                var fileList = form.FileList;
                fileList = AddListToCombox(fileList);
                cbText1.SelectedItem = fileList;

                itemsList.Save();
                RefreshDifferences();
            }
            else
                return;

        }
        /// <summary>
        /// Inserts or removes an exclusion rule from the currently selected node
        /// </summary>
        private void ToggleSelectedNodeRule()
        {
            var currentItemList = GetCurrentItemList(false);
            if (currentItemList == null)
                return;
            ExtendedTreeView treeView = GetCurrentTreeView();
            List<ComparableItem> items = new List<ComparableItem>();
            foreach (ComparableItemTreeNode node in treeView.SelectedNodes)
                items.Add(node.Item);
            List<ComparableItem> rootItems = currentItemList.Rules.Toggle(items.ToArray());
            foreach (var item in rootItems)
            {
                item.ResetRules();
                item.RelatedItem.ResetRules();
            }
            RefreshColors();
            RefreshErrorCount();
        }

        /// <summary>
        /// Remove all rules from currently selected root node(s)
        /// </summary>
        private void RemoveAllRules()
        {
            ExtendedTreeView treeView = GetCurrentTreeView();
            List<ComparableItem> rootItems = new List<ComparableItem>();
            foreach (ComparableItemTreeNode node in treeView.SelectedNodes)
            {
                var root = node.Item.Root;
                if (!rootItems.Contains(root))
                    rootItems.Add(root);
            }

            GetCurrentItemList(true).Rules.Remove(rootItems.ToArray());
            RefreshColors();
            RefreshErrorCount();
        }
        /// <summary>
        /// Copies the selected node's text to the clipboard
        /// </summary>
        private void CopyToClipboard()
        {
            try
            {
                StringBuilder builder = new StringBuilder();
                List<ComparableItemTreeNode> allNodes = OrderedSelectedNodes(GetCurrentTreeView().Nodes);
                foreach (var node in allNodes)
                    builder.AppendLine(node.Text);
                string value = builder.ToString().Trim("\r\n".ToCharArray());
                if (!String.IsNullOrEmpty(value))
                    Clipboard.SetText(value);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        /// <summary>
        /// This method synchronizes the selected nodes values between source and destination
        /// </summary>
        private void EnforceValue()
        {
            ExtendedTreeView treeview = GetCurrentTreeView();
            List<ComparableItem> itemsToRefresh = new List<ComparableItem>();
            string itemsToSave = "";
            foreach (ComparableItemTreeNode node in treeview.SelectedNodes)
            {
                ComparableItem item = node.Item;
                if (item != null)
                {
                    if (!itemsToRefresh.Contains(item.Root))
                    {
                        itemsToSave += "\r\n" + item.Root.RelatedItem.Text;
                        itemsToRefresh.Add(item.Root);
                    }
                }
            }

            if (itemsToRefresh.Count == 0)
                return;
            if (Settings.Current.AskBeforeSaving && MessageBox.Show(string.Format (Resources.Resources.SaveItemsQuestion , itemsToSave), Resources.Resources.SavingCaption, MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
                return;

            foreach (ComparableItemTreeNode node in treeview.SelectedNodes)
            {
                ComparableItem item = node.Item;
                if (item != null)
                    item.Copy(false);
            }

            foreach (var item in itemsToRefresh)
                item.RelatedItem.Save();
            RefreshDifferences(true);
        }
        #endregion auxiliary methods

        ItemNamePairList GetCurrentItemList(bool createIfNotExists)
        {
            ItemNamePairList list = cbText1.SelectedItem as ItemNamePairList;
            if (list == null && createIfNotExists)
            {
                list = new ItemNamePairList();
                itemsList.AddNew(list);
                cbText1.Items.Insert(0, list);
                cbText2.Items.Insert(0, list);
                //cbText1.SelectedIndex = 0;

            }
            return list;

        }
        ItemNamePairList CurrentItemList2
        {
            get
            {
                var value = cbText2.SelectedItem as ItemNamePairList;
                if (value == null)
                    return GetCurrentItemList(false);
                else
                    return value;
            }
        }
        private string LoadFile(string file)
        {
            OpenFileDialog fd = new OpenFileDialog();
            fd.Multiselect = true;
            if (!String.IsNullOrEmpty(file))
                fd.FileName = file;
            if (fd.ShowDialog() == DialogResult.OK)
            {
                return ItemNamePairList.ListToText(fd.FileNames);
            }
            return null;
        }
        private ItemNamePairList AddListToCombox(ItemNamePairList fileList)
        {
            string name1 = fileList.Name1;
            string name2 = fileList.Name2;
            for (int j = 0; j < cbText1.Items.Count; j++)
            {
                ItemNamePairList nextList = cbText1.Items[j] as ItemNamePairList;
                if (name1 == nextList.Name1 && name2 == nextList.Name2)
                    return nextList;
            }

            cbText1.Items.Add(fileList);
            cbText2.Items.Add(fileList);
            return fileList;
        }
        private void RefreshCombos(bool forceRefresh)
        {
            cbText1.ValueMember = "Name1";
            cbText2.ValueMember = "Name2";

            if (forceRefresh || itemsList.Count != cbText1.Items.Count)
            {
                itemsList.Sort();
                cbText1.Items.Clear();
                cbText2.Items.Clear();
                foreach (var fileList in itemsList)
                    AddListToCombox(fileList);
            }

            if (!RunningAsCommandLine)
            {
                if (cbText1.Items.Count > 0)
                {
                    if (cbText1.SelectedIndex != 0)
                        cbText1.SelectedIndex = 0;
                    else
                        RefreshDifferences();
                }
            }

        }
        public void RefreshColors()
        {
            foreach (ComparableItemTreeNode node in tree1.Nodes)
                node.ApplyStyle(node.Item, true);
            foreach (ComparableItemTreeNode node in tree2.Nodes)
                node.ApplyStyle(node.Item, true);
        }
        private void SetNodeAsChanged(ComparableItemTreeNode node)
        {
            node.NodeChanged();
        }
        private void FileWathcherJob(ManagedThread thread)
        {
            List<ComparableItemTreeNode> nodes;

            try
            {
                while (!thread.CancellationPending)
                {
                    nodes = new List<ComparableItemTreeNode>();
                    lock (nodesToPoll)
                    {
                        nodes.AddRange(nodesToPoll);
                    }
                    foreach (ComparableItemTreeNode node in nodes)
                    {
                        try
                        {
                            if (!ComparisonEngine.Current.IsProcessing && node.Item.HasChanged)
                            {
                                this.Execute(SetNodeAsChanged, node);
                                lock (nodesToPoll)
                                {
                                    if (nodesToPoll.Contains(node))
                                        nodesToPoll.Remove(node);
                                }
                            }
                        }
                        catch
                        {
                        }
                    }
                    System.Threading.Thread.Sleep(1000);
                }
            }
            finally
            {
            }
        }
        protected virtual string NormalizeList(string text)
        {
            return text;
        }
        private void Synchronize(TreeView sourceTree, TreeView destinationTree, ComparableItemTreeNode sourceNode)
        {
            if (sourceNode == null || sourceNode.Tag == null)
                return;

            TreeNode destinationNode = sourceNode.RelatedNode;
            if (destinationNode == null)
                return;
            if (destinationNode.IsExpanded != sourceNode.IsExpanded)
            {
                if (sourceNode.IsExpanded)
                    destinationNode.Expand();
                else
                    destinationNode.Collapse();
            }

            if (destinationNode.IsSelected != sourceNode.IsSelected && sourceNode.IsSelected)
                destinationTree.SelectedNode = destinationNode;

        }
        private ExtendedTreeView GetCurrentTreeView()
        {
            ExtendedTreeView treeview = selectedTreeView;
            //Bug in contextmenustrip.SourceControl in hierarchical menus
            if (treeview == null)
                treeview = ActiveControl as ExtendedTreeView;
            if (treeview == null)
                treeview = tree1;
            return treeview;
        }
        public ComparableItem[] FocusedSelectedItems()
        {
            var tree = GetCurrentTreeView();
            List<ComparableItem> items = new List<ComparableItem>();
            foreach (ComparableItemTreeNode node in tree.SelectedNodes)
            {
                if (node.Item != null)
                    items.Add(node.Item);
            }
            ComparableItemTreeNode selectedNode = GetCurrentTreeView().SelectedNode as ComparableItemTreeNode;
            if (selectedNode != null)
            {
                if (selectedNode.Item != null)
                {
                    if (!items.Contains(selectedNode.Item))
                        items.Add(selectedNode.Item);
                }
            }
            return items.ToArray();
        }
        public ComparableItem[] UnfocusedSelectedItems()
        {
            var tree = GetCurrentTreeView();
            if (tree == tree1)
                tree = tree2;
            else
                tree = tree1;
            List<ComparableItem> items = new List<ComparableItem>();
            foreach (ComparableItemTreeNode node in tree.SelectedNodes)
            {
                if (node.Item != null)
                    items.Add(node.Item);
            }
            return items.ToArray();
        }
        public ComparableItem[] AllSelectedItems()
        {
            List<ComparableItem> items = new List<ComparableItem>();
            foreach (ComparableItemTreeNode node in tree1.SelectedNodes)
            {
                if (node.Item != null)
                    items.Add(node.Item);
            }
            foreach (ComparableItemTreeNode node in tree2.SelectedNodes)
            {
                if (node.Item != null)
                    items.Add(node.Item);
            }
            ComparableItemTreeNode selectedNode = GetCurrentTreeView().SelectedNode as ComparableItemTreeNode;
            if (selectedNode != null)
            {
                if (selectedNode.Item != null)
                {
                    if (!items.Contains(selectedNode.Item))
                        items.Add(selectedNode.Item);
                }
            }

            return items.ToArray();

        }
        public void RefreshDifferences()
        {
            RefreshDifferences(false);
        }
        public void RefreshDifferences(bool cancelRefreshIfProcessing)
        {
            lock (nodesToPoll)
            {
                nodesToPoll.Clear();
            }
            var list = itemsList.Add(Items1, Items2);
            ComparisonEngine.Current.RefreshDifferences(list, cancelRefreshIfProcessing);
        }
        private void toolStripSplitButton1_ButtonClick(object sender, EventArgs e)
        {
            RefreshDifferences();
        }
        private void cbMatchCase_CheckedChanged(object sender, EventArgs e)
        {
            ChangeFilter(new Filter(txtFilter.Text, !cbMatchCase.Checked));
        }
        private List<ComparableItem> SelectedRootItems
        {
            get
            {
                List<ComparableItem> rootItems = new List<ComparableItem>();
                foreach (ComparableItemTreeNode node in GetCurrentTreeView().SelectedNodes)
                {
                    var item = node.Item.Root;
                    if (!rootItems.Contains(item))
                        rootItems.Add(item);
                }
                return rootItems;
            }
        }
        private void createNewItemsList_Click(object sender, EventArgs e)
        {
            if (IsProcessing)
                return;
            List<ItemId> items1 = new List<ItemId>();
            List<ItemId> items2 = new List<ItemId>();

            foreach (var item in SelectedRootItems)
            {
                items1.Add(new ItemId(item.Text));
                items2.Add(new ItemId(item.RelatedItem.Text));
            }

            if (items1.Count > 0)
            {
                var newList = new ItemNamePairList(items1.ToArray(), items2.ToArray());
                itemsList.Add(newList);
                cbText1.SelectedItem = newList;
                ManageItemsList();
            }
        }
        private void removeSelectedItemsList_Click(object sender, EventArgs e)
        {
            if (IsProcessing)
                return;
            var rootItems = SelectedRootItems;
            if (rootItems.Count > 0)
            {
                bool changed = false;
                foreach (var item in rootItems)
                    changed |= GetCurrentItemList(true).Remove(item.Text, item.RelatedItem.Text);

                if (changed)
                {
                    RefreshCombos(true);
                }
            }
        }
        private void toolStripMatchToCombo_SelectedIndexChanged(object sender, EventArgs e)
        {

        }
        public void AddItemsToSelection(string[] items)
        {
            if (tree1 == GetCurrentTreeView())
            {
                GetCurrentItemList(true).Add(items);
            }
            else
            {
                GetCurrentItemList(true).AddItems2(ItemNamePairList.StringsToItems(items));
            }
            RefreshCombos(true);

        }

       

    }
}
